#include "stmflash.h"


#define FLASH_WAITETIME     50000
#define PAGE_SIZE           (FLASH_PAGE_SIZE)

extern void    FLASH_PageErase(uint32_t PageAddress);
static uint8_t _erase_page(uint16_t page)
{
    uint32_t address = STMFLASH_GET_ADDR(page);
    FLASH_PageErase(address);   //擦除这个扇区
    FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
    CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
    return 0;
}

static uint16_t _read_16bit(uint32_t address)
{
    return *(__IO uint16_t *)address;
}

static uint8_t _write_16bit(uint32_t address, uint16_t data)
{
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, address, data) != HAL_OK)
    {
        Error_Handler();
    }
    return 0;
}

static void _erase_addr(uint32_t address, uint32_t size)
{
    if(address%PAGE_SIZE > 0)
        return;

    uint32_t count = 0;
    uint16_t page = 0;

    HAL_FLASH_Unlock();
    while(count < size)
    {
        page = STMFLASH_GET_PAGE(address);
        _erase_page(page);
        address += PAGE_SIZE;
        count += PAGE_SIZE;
    }
    HAL_FLASH_Lock();
}

static void _write_page(uint16_t page, uint8_t *page_data)
{
    uint8_t *ptdata = page_data;
    uint16_t num = PAGE_SIZE/2;
    uint32_t address = STMFLASH_GET_ADDR(page);

    static uint32_t systick = 0;
    while(system_get_tick() - systick < 10);

    HAL_FLASH_Unlock();
    _erase_page(page);
    for(int i = 0; i < num; i++)
    {
        _write_16bit(address,*((uint16_t *)ptdata));
        address += 2;
        ptdata += 2;
    }
    HAL_FLASH_Lock();

    systick = system_get_tick();
}

static void _read_page(uint16_t page, uint8_t *page_data)
{
    uint8_t *ptdata = page_data;
    uint16_t num = PAGE_SIZE/2;
    uint32_t address = STMFLASH_GET_ADDR(page);

    uint16_t data16 = 0;
    for(int i = 0; i < num; i++)
    {
        data16 = _read_16bit(address);
        *((uint16_t *)ptdata) = data16;
        ptdata += 2;
        address += 2;
    }
}

#if 0
uint32_t stmflash_read(uint32_t address, uint8_t *buffer, uint32_t size)
{
    return 0;
}
uint32_t stmflash_write(uint32_t address, uint8_t *buffer, uint32_t size)
{
    return 0;
}
void stmflash_erase(uint32_t address, uint32_t size)
{

}
void stmflash_flush(void)
{

}
int stmflash_init(void)
{
    return 0;
}
#else
// cache
////////////////////////////////////////////////////////////////////////
#define SECTOR_SIZE             (PAGE_SIZE)
#define FLASH_GET_SECTOR(a)     STMFLASH_GET_PAGE(a)
#define FLASH_GET_ADDR(s)       STMFLASH_GET_ADDR(s)

#define SECTOR_STATE_INIT     (0)
#define SECTOR_STATE_DIRTY    (0x12)

typedef struct
{
    uint8_t state;
    uint16_t sector;
    uint8_t data[SECTOR_SIZE];
}SECTOR_CACHE;
static SECTOR_CACHE _sector_cache;

static int _read_page_cache(uint32_t address, uint8_t *buffer, uint32_t size)
{
    if(buffer == NULL)
        return -1;

    uint16_t read_sector = FLASH_GET_SECTOR(address);
    if(read_sector != FLASH_GET_SECTOR(address+size-1))
        return -3;

    if(read_sector != _sector_cache.sector)
    {
        if(_sector_cache.state == SECTOR_STATE_DIRTY)
        {
            _write_page(_sector_cache.sector, _sector_cache.data);
        }
        _sector_cache.sector = read_sector;
        _sector_cache.state = SECTOR_STATE_INIT;
        _read_page(_sector_cache.sector, _sector_cache.data);
    }
    uint16_t offset = address%SECTOR_SIZE;
    memcpy(buffer,&(_sector_cache.data[offset]),size);

    return 1;
}

static int _write_page_cache(uint32_t address, const uint8_t *buffer, uint32_t size)
{
    if(buffer == NULL)
        return -1;

    uint16_t write_sector = FLASH_GET_SECTOR(address);
    if(write_sector != FLASH_GET_SECTOR(address+size-1))
        return -3;

    if(write_sector != _sector_cache.sector)
    {
        if(_sector_cache.state == SECTOR_STATE_DIRTY)
        {
            _write_page(_sector_cache.sector, _sector_cache.data);
        }
        _sector_cache.sector = write_sector;
        _sector_cache.state = SECTOR_STATE_INIT;
        _read_page(_sector_cache.sector, _sector_cache.data);
    }
    uint16_t offset = address%SECTOR_SIZE;
    memcpy(&(_sector_cache.data[offset]),buffer,size);
    _sector_cache.state = SECTOR_STATE_DIRTY;

    return 1;
}

uint32_t stmflash_read(uint32_t address, uint8_t *buffer, uint32_t size)
{
    while(size > 0)
    {
        uint16_t remain = SECTOR_SIZE - address%SECTOR_SIZE;
        uint16_t count = (size<remain)?(size):(remain);

        _read_page_cache(address,buffer,count);
        address += count;
        buffer += count;
        size -= count;
    }
    return size;
}

uint32_t stmflash_write(uint32_t address, uint8_t *buffer, uint32_t size)
{
    while(size > 0)
    {
        uint16_t remain = SECTOR_SIZE - address%SECTOR_SIZE;
        uint16_t count = (size<remain)?(size):(remain);

        _write_page_cache(address,buffer,count);
        address += count;
        buffer += count;
        size -= count;
    }
    return size;
}

void stmflash_erase(uint32_t address, uint32_t size)
{
    _erase_addr(address, size);
}

void stmflash_flush(void)
{
    if(_sector_cache.state == SECTOR_STATE_DIRTY)
    {
        _write_page(_sector_cache.sector, _sector_cache.data);
        _sector_cache.state = SECTOR_STATE_INIT;
    }
}

int stmflash_init(void)
{
    _sector_cache.sector = 0xffff;
    _sector_cache.state = SECTOR_STATE_INIT;
    return 1;
}
#endif
